package com.piggy.sys.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendBatchSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendBatchSmsResponse;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.piggy.common.core.constant.Constants;
import com.piggy.common.core.enums.CacheKeyEnums;
import com.piggy.common.core.exception.base.BaseException;
import com.piggy.common.core.utils.PageUtils;
import com.piggy.common.core.utils.sms.AliSendSmsUtils;
import com.piggy.common.core.utils.sms.SmsConfig;
import com.piggy.common.core.web.page.PagePlus;
import com.piggy.common.core.web.page.TableDataInfo;
import com.piggy.common.redis.cache.CacheUtils;
import com.piggy.common.satoken.utils.SecurityUtils;
import com.piggy.sys.api.bo.SmsNotify;
import com.piggy.sys.api.bo.SysSmsBo;
import com.piggy.sys.api.bo.SysSmsQueryBo;
import com.piggy.sys.api.domain.SysDictData;
import com.piggy.sys.api.enums.SmsTemplateEnum;
import com.piggy.sys.api.vo.SysSmsVo;
import com.piggy.sys.constant.SmsConstant;
import com.piggy.sys.domain.SysSms;
import com.piggy.sys.enums.SmsConfigEnum;
import com.piggy.sys.mapper.SysSmsMapper;
import com.piggy.sys.service.ISysConfigService;
import com.piggy.sys.service.ISysDictTypeService;
import com.piggy.sys.service.ISysSmsService;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 消息模板Service业务层处理
 *
 * @author shark
 * @date 2021-08-16
 */
@Slf4j
@Service
public class SysSmsServiceImpl extends ServiceImpl<SysSmsMapper, SysSms> implements ISysSmsService {
    @Resource
    ISysConfigService configService;

    @Resource
    ISysDictTypeService dictTypeService;

    public static Map<String, SysDictData> SMS_TEMPLATE_DICT = new HashMap<>();
    public static Map<String, SysDictData> SMS_CONFIG_DICT = new HashMap<>();

    @Data
    @Accessors(chain = true)
    public static class SmsTemplate {
        private String code;
        private String subject;
    }

    @PostConstruct
    public void init() {

        List<SysDictData> smsTemplateList = dictTypeService.selectDictDataByType(SmsConstant.SMS_TEMPLATE);
        if (CollUtil.isNotEmpty(smsTemplateList)) {
            smsTemplateList.stream().map(Any-> SMS_TEMPLATE_DICT.put(Any.getDictLabel(), Any)).collect(Collectors.toList());
        }

        List<SysDictData> smsConfigList = dictTypeService.selectDictDataByType(SmsConstant.SMS_CONFIG);
        if (CollUtil.isNotEmpty(smsConfigList)) {
            smsConfigList.stream().map(Any-> SMS_CONFIG_DICT.put(Any.getDictLabel(), Any)).collect(Collectors.toList());
        }

    }

    @SneakyThrows
    @Override
    public Boolean sendSms(SysSmsBo bo) {
        //获取短信云配置
        SmsConfig cloudConfig = getConfig();
        //是否开启短信发送
        if (!cloudConfig.isOpenSend()) return true;

        Client client = AliSendSmsUtils.createClient(cloudConfig.getAliyunAccessKeyId(), cloudConfig.getAliyunAccessKeySecret());
        SendSmsRequest sendReq = new SendSmsRequest()
                .setPhoneNumbers(bo.getPhoneNumber())
                .setTemplateCode(bo.getTemplateCode())
                .setSignName(bo.getSignName())
                .setTemplateParam(bo.getTemplateParam());

        //send
        SendSmsResponse sendResp = client.sendSms(sendReq);
        String code = sendResp.body.code;

        //database
        SysSms sms = new SysSms()
                .setAccessKeyId(cloudConfig.getAliyunAccessKeyId())
                .setAccessKeySecret(cloudConfig.getAliyunAccessKeySecret())
                .setCreateTime(new Date())
                .setLink(bo.getPhoneNumber())
                .setCreateBy(bo.getSendUserId())
                .setUserId(bo.getUserId())
                //0-站内信 1-邮件 2-短信
                .setType(bo.getType())
                .setSignName(bo.getSignName())
                .setStatus(StrUtil.compareIgnoreCase(code, "OK", false) == 0 ? 0 : 1)
                .setTemplateId(bo.getTemplateCode())
                .setSubject(bo.getSubject())
                .setSendResult(JSONUtil.toJsonStr(sendResp.body))
                .setParams(bo.getTemplateParam());

        validEntityBeforeSave(sms);

        //result
        if (!StrUtil.equals(code, "OK")) {
            log.error("错误信息: {}",sendResp.body.message);
            return false;
        }

        return true;

    }

    @Override
    public Boolean autoCodeSendSms(String phone, String autoCode) {


        String tempParam = "{\"code\":\"" + autoCode + "\"}";

        SysSmsBo bo = new SysSmsBo()
                .setSubject("发送短信")
                .setType(2)
                .setUserId(1L)
                .setSendUserId(1L)
                .setSignName("XXXX")
                .setTemplateCode("SMS_226786606")
                .setTemplateParam(tempParam)
                .setPhoneNumber(phone);
        this.sendSms(bo);

        return true;

    }

    @Override
    public Boolean userRegSendSms(String phone) {

        Integer randomInt = RandomUtil.randomInt(1000, 10000);
        String tempParam = "{\"code\":\"" + randomInt + "\"}";
        SMS_CONFIG_DICT.get(SmsConfigEnum.SIGN.getCode());
        String json = SMS_TEMPLATE_DICT.get(SmsTemplateEnum.VERIFY.getCode()).getDictValue();
        SmsTemplate template = JSON.parseObject(json, SmsTemplate.class);

        SysSmsBo bo = new SysSmsBo()
                .setSubject(template.subject)
                .setType(2)
                .setUserId(1L)
                .setSignName(SMS_CONFIG_DICT.get(SmsConfigEnum.SIGN.getCode()).getDictValue())
                .setSendUserId(1L)
                .setTemplateCode(template.code)
                .setTemplateParam(tempParam)
                .setPhoneNumber(phone);
        this.sendSms(bo);

        // 保存验证码信息
        String verifyKey = CacheKeyEnums.RegUserSms.getKey(phone);
        CacheUtils.cache(randomInt, verifyKey, CacheKeyEnums.RegUserSms.getTime(), TimeUnit.MINUTES);
        return true;

    }

    @Override
    public Boolean createAccount(String phone, String passwd, String company, String regTime) {
        //String content = String.format("已创建账号 账号：%s 密码：%s [%s]", phone, passwd, company);

        String json = SMS_TEMPLATE_DICT.get(SmsTemplateEnum.CREATE_ACCOUNT.getCode()).getDictValue();
        SmsTemplate template = JSON.parseObject(json, SmsTemplate.class);

        Map<String,String> params = new HashMap<>();
        params.put("p1", company);
        params.put("p2", phone);
        params.put("p3", passwd);
        params.put("p4", regTime);

        SysSmsBo bo = new SysSmsBo()
                .setSubject(template.subject)
                .setType(2)
                .setUserId(1L)
                .setSignName(SMS_CONFIG_DICT.get(SmsConfigEnum.SIGN.getCode()).getDictValue())
                .setSendUserId(1L)
                .setTemplateCode(template.code)
                .setTemplateParam(JSON.toJSONString(params))
                .setPhoneNumber(phone);
        this.sendSms(bo);
        return true;
    }

    @Override
    public Boolean resetPasswd(String phone, String passwd, String company) {
        String json = SMS_TEMPLATE_DICT.get(SmsTemplateEnum.RESET_PASSWD.getCode()).getDictValue();
        SmsTemplate template = JSON.parseObject(json, SmsTemplate.class);

        Map<String,String> params = new HashMap<>();
        params.put("p1", passwd);
        params.put("p2", company);

        SysSmsBo bo = new SysSmsBo()
                .setSubject(template.subject)
                .setType(2)
                .setUserId(1L)
                .setSignName(SMS_CONFIG_DICT.get(SmsConfigEnum.SIGN.getCode()).getDictValue())
                .setSendUserId(1L)
                .setTemplateCode(template.code)
                .setTemplateParam(JSON.toJSONString(params))
                .setPhoneNumber(phone);
        this.sendSms(bo);
        return true;
    }

    @Override
    public Boolean waybillNotify(String phone, String waybillNo, String status, String nextStatus) {
        String json = SMS_TEMPLATE_DICT.get(SmsTemplateEnum.WAYBILL_NOTIFY.getCode()).getDictValue();
        SmsTemplate template = JSON.parseObject(json, SmsTemplate.class);

        Map<String,String> params = new HashMap<>();
        params.put("p1", waybillNo);
        params.put("p2", status);
        params.put("p3", nextStatus);

        SysSmsBo bo = new SysSmsBo()
                .setSubject(template.subject)
                .setType(2)
                .setUserId(1L)
                .setSignName(SMS_CONFIG_DICT.get(SmsConfigEnum.SIGN.getCode()).getDictValue())
                .setSendUserId(1L)
                .setTemplateCode(template.code)
                .setTemplateParam(JSON.toJSONString(params))
                .setPhoneNumber(phone);
        this.sendSms(bo);
        return true;
    }

    @Override
    public Boolean scheduleNotify(String phone, String scheduleNo, String status, String nextStatus) {
        String json = SMS_TEMPLATE_DICT.get(SmsTemplateEnum.SCHEDULE_NOTIFY.getCode()).getDictValue();
        SmsTemplate template = JSON.parseObject(json, SmsTemplate.class);

        Map<String,String> params = new HashMap<>();
        params.put("p1", scheduleNo);
        params.put("p2", status);
        params.put("p3", nextStatus);

        SysSmsBo bo = new SysSmsBo()
                .setSubject(template.subject)
                .setType(2)
                .setUserId(1L)
                .setSignName(SMS_CONFIG_DICT.get(SmsConfigEnum.SIGN.getCode()).getDictValue())
                .setSendUserId(1L)
                .setTemplateCode(template.code)
                .setTemplateParam(JSON.toJSONString(params))
                .setPhoneNumber(phone);
        this.sendSms(bo);
        return true;
    }

    @Override
    public Boolean smsNotify(String phone, SmsNotify smsNotify) {
        String json = SMS_TEMPLATE_DICT.get(smsNotify.getTemplateCode()).getDictValue();
        SmsTemplate template = JSON.parseObject(json, SmsTemplate.class);

        SysSmsBo bo = new SysSmsBo()
                .setSubject(template.subject)
                .setType(2)
                .setUserId(1L)
                .setSignName(SMS_CONFIG_DICT.get(SmsConfigEnum.SIGN.getCode()).getDictValue())
                .setSendUserId(1L)
                .setTemplateCode(template.code)
                .setTemplateParam(JSON.toJSONString(smsNotify.getParams()))
                .setPhoneNumber(phone);
        this.sendSms(bo);
        return true;
    }

    @Override
    public SmsConfig getConfig() {
        String cloudConfig = configService.selectConfigByKey(Constants.ALICLOUDSMS);
        return JSON.parseObject(cloudConfig, SmsConfig.class);
    }

    @SneakyThrows
    @Override
    public Boolean sendBatchSms(List<SysSmsBo> smsList, String templateCode) {
        List<String> phoneNumbers = smsList.parallelStream().map(SysSmsBo::getPhoneNumber).collect(Collectors.toList());
        List<String> signNames = smsList.parallelStream().map(SysSmsBo::getSignName).collect(Collectors.toList());
        List<String> templateParams = smsList.parallelStream().map(SysSmsBo::getTemplateParam).collect(Collectors.toList());

        SmsConfig cloudConfig = getConfig();
        if (!cloudConfig.isOpenSend()) return true;
        com.aliyun.dysmsapi20170525.Client client = AliSendSmsUtils.createClient(cloudConfig.getAliyunAccessKeyId(), cloudConfig.getAliyunAccessKeySecret());
        SendBatchSmsRequest sendReq = new SendBatchSmsRequest()
                .setPhoneNumberJson(JSONUtil.toJsonStr(phoneNumbers))
                .setSignNameJson(JSONUtil.toJsonStr(signNames))
                .setTemplateCode(templateCode)
                .setTemplateParamJson(JSONUtil.toJsonStr(templateParams));

        SendBatchSmsResponse sendResp = client.sendBatchSms(sendReq);
        String code = sendResp.body.code;

        List<SysSms> sysSmsBos = new ArrayList<>();
        smsList.forEach(t -> {
            //database
            SysSms sms = new SysSms()
                    .setAccessKeyId(cloudConfig.getAliyunAccessKeyId())
                    .setAccessKeySecret(cloudConfig.getAliyunAccessKeySecret())
                    .setCreateTime(new Date())
                    .setLink(t.getPhoneNumber())
                    .setCreateBy(SecurityUtils.getUserId())
                    .setUserId(t.getUserId())
                    //0-站内信 1-邮件 2-短信
                    .setType(t.getType())
                    .setSignName(t.getSignName())
                    .setStatus(StrUtil.compareIgnoreCase(code, "OK", false) == 0 ? 0 : 1)
                    .setTemplateId(t.getTemplateCode())
                    .setSubject(t.getSubject())
                    .setSendResult(JSONUtil.toJsonStr(sendResp.body))
                    .setParams(t.getTemplateParam());
            sysSmsBos.add(sms);
        });
        this.saveBatch(sysSmsBos);

        if (!com.aliyun.teautil.Common.equalString(code, "OK")) {
            Console.error("错误信息: " + sendResp.body.message + "");
            return false;
        }
        return true;
    }

    @SneakyThrows
    @Override
    public Boolean sendBatchSms(SysSmsBo bo) {
        SmsConfig cloudConfig = getConfig();
        if (!cloudConfig.isOpenSend()) return true;
        com.aliyun.dysmsapi20170525.Client client = AliSendSmsUtils.createClient(cloudConfig.getAliyunAccessKeyId(), cloudConfig.getAliyunAccessKeySecret());
        SendBatchSmsRequest sendReq = new SendBatchSmsRequest()
                .setPhoneNumberJson(JSONUtil.toJsonStr(bo.getPhoneNumbers()))
                .setSignNameJson(JSONUtil.toJsonStr(bo.getSignNames()))
                .setTemplateCode(bo.getTemplateCode())
                .setTemplateParamJson(JSONUtil.toJsonStr(bo.getTemplateParams()));

        SendBatchSmsResponse sendResp = client.sendBatchSms(sendReq);
        String code = sendResp.body.code;

        //database
        List<SysSms> sysSmsBos = new ArrayList<>();
        String sendResult = JSONUtil.toJsonStr(sendResp.body);
        for (int i = 0; i < bo.getPhoneNumbers().size(); i++) {
            SysSms sms = new SysSms()
                    .setAccessKeyId(cloudConfig.getAliyunAccessKeyId())
                    .setAccessKeySecret(cloudConfig.getAliyunAccessKeySecret())
                    .setCreateTime(new Date())
                    .setLink(bo.getPhoneNumbers().get(i))
                    .setCreateBy(SecurityUtils.getUserId())
                    .setUserId(bo.getUserId())
                    //0-站内信 1-邮件 2-短信
                    .setType(bo.getType())
                    .setSignName(bo.getSignNames().get(i))
                    .setStatus(StrUtil.compareIgnoreCase(code, "OK", false) == 0 ? 0 : 1)
                    .setTemplateId(bo.getTemplateCode())
                    .setSubject(bo.getSubject())
                    .setSendResult(sendResult)
                    .setParams(bo.getTemplateParams().get(i));
            sysSmsBos.add(sms);
        }
        this.saveBatch(sysSmsBos);

        if (!com.aliyun.teautil.Common.equalString(code, "OK")) {
            Console.error("错误信息: " + sendResp.body.message + "");
            return false;
        }
        return true;
    }


    @Override
    public SysSmsVo queryById(Long id) {
        return getVoById(id, SysSmsVo.class);
    }

    @Override
    public TableDataInfo<SysSmsVo> queryPageList(SysSmsQueryBo bo) {
        PagePlus<SysSms, SysSmsVo> result = pageVo(PageUtils.buildPagePlus(true), buildQueryWrapper(bo), SysSmsVo.class);
        return PageUtils.buildDataInfo(result);
    }

    @Override
    public List<SysSmsVo> queryList(SysSmsQueryBo bo) {
        return listVo(buildQueryWrapper(bo), SysSmsVo.class);
    }

    private LambdaQueryWrapper<SysSms> buildQueryWrapper(SysSmsQueryBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysSms> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, SysSms::getUserId, bo.getUserId());
        lqw.eq(bo.getTemplateId() != null, SysSms::getTemplateId, bo.getTemplateId());
        lqw.eq(StrUtil.isNotBlank(bo.getLink()), SysSms::getLink, bo.getLink());
        lqw.eq(bo.getStatus() != null, SysSms::getStatus, bo.getStatus());
        lqw.eq(bo.getType() != null, SysSms::getType, bo.getType());
        lqw.eq(StrUtil.isNotBlank(bo.getAccessKeyId()), SysSms::getAccessKeyId, bo.getAccessKeyId());
        lqw.eq(StrUtil.isNotBlank(bo.getAccessKeySecret()), SysSms::getAccessKeySecret, bo.getAccessKeySecret());
        lqw.like(StrUtil.isNotBlank(bo.getSignName()), SysSms::getSignName, bo.getSignName());
        return lqw;
    }

    /**
     * 保存前的数据校验
     *
     * @param entity 实体类数据
     */
    private void validEntityBeforeSave(SysSms entity) {
        //TODO 做一些数据校验,如唯一约束
        this.save(entity);
    }

    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return removeByIds(ids);
    }

    @Override
    public Boolean sendMail(String content, String subject, List<String> params, String receiver, File[] files) {

        String emailInfo = configService.selectConfigByKey(Constants.EMAILINFO);
        if (StrUtil.isBlank(emailInfo)) {
            throw new BaseException("请联系管理员在系统配置中心配置邮箱信息！");
        }
        JSONObject emailJson = JSONUtil.parseObj(emailInfo);
        if (StrUtil.isBlankIfStr(emailJson.getStr("email")) || StrUtil.isBlankIfStr(emailJson.getStr("passWord"))) {
            throw new BaseException("请联系管理员在系统配置中心完善邮箱账号密码！");
        }
        String email = emailJson.getStr("email");
        String passWord = emailJson.getStr("passWord");


        //配置邮箱信息
        MailAccount account = new MailAccount();
        account.setHost("smtp.exmail.qq.com");
        account.setPort(465);
        account.setAuth(true);
        account.setSslEnable(true);
        account.setFrom(email);
        account.setUser(email);
        account.setPass(passWord);

        for (String param : params) {
            content = StrUtil.format(content, param);
        }
        try {
            MailUtil.send(account, receiver, subject, content, true, files);
        } catch (Exception e) {
            throw new BaseException("请联系平台管理员检查邮箱信息是否有效！");
        }

        SysSms sysSms = new SysSms();
        sysSms.setAccessKeyId(email)
                .setAccessKeyId(passWord)
                .setSubject(subject)
                .setLink(receiver)
                .setType(1)
                .setStatus(0)
                .setParams(params.toString())
                .setCreateBy(SecurityUtils.getUserId())
                .setSignName(subject);

        boolean save = save(sysSms);

        return save;
    }
}
